{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# utils"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import vectorbt as vbt\n",
    "\n",
    "from vectorbt.utils import checks, config, decorators, attr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "Collapsed": "false"
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "from numba import njit"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "i1\n",
      "x1    1\n",
      "dtype: int64\n",
      "i2\n",
      "x2    1\n",
      "y2    2\n",
      "z2    3\n",
      "dtype: int64\n",
      "c3  a3\n",
      "i3    \n",
      "x3   1\n",
      "c4  a4\n",
      "i4    \n",
      "x4   1\n",
      "y4   2\n",
      "z4   3\n",
      "c5  a5  b5  c5\n",
      "i5            \n",
      "x5   1   2   3\n",
      "c6  a6  b6  c6\n",
      "i6            \n",
      "x6   1   2   3\n",
      "y6   4   5   6\n",
      "z6   7   8   9\n",
      "c7    a7 b7 c7\n",
      "c8    a8 b8 c8\n",
      "i7 i8         \n",
      "x7 x8  1  2  3\n",
      "y7 y8  4  5  6\n",
      "z7 z8  7  8  9\n"
     ]
    }
   ],
   "source": [
    "v1 = 0\n",
    "a1 = np.array([1])\n",
    "a2 = np.array([1, 2, 3])\n",
    "a3 = np.array([[1, 2, 3]])\n",
    "a4 = np.array([[1], [2], [3]])\n",
    "a5 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n",
    "sr1 = pd.Series([1], index=pd.Index(['x1'], name='i1'))\n",
    "print(sr1)\n",
    "sr2 = pd.Series([1, 2, 3], index=pd.Index(['x2', 'y2', 'z2'], name='i2'))\n",
    "print(sr2)\n",
    "df1 = pd.DataFrame(\n",
    "    [[1]], \n",
    "    index=pd.Index(['x3'], name='i3'), \n",
    "    columns=pd.Index(['a3'], name='c3'))\n",
    "print(df1)\n",
    "df2 = pd.DataFrame(\n",
    "    [[1], [2], [3]], \n",
    "    index=pd.Index(['x4', 'y4', 'z4'], name='i4'), \n",
    "    columns=pd.Index(['a4'], name='c4'))\n",
    "print(df2)\n",
    "df3 = pd.DataFrame(\n",
    "    [[1, 2, 3]], \n",
    "    index=pd.Index(['x5'], name='i5'), \n",
    "    columns=pd.Index(['a5', 'b5', 'c5'], name='c5'))\n",
    "print(df3)\n",
    "df4 = pd.DataFrame(\n",
    "    [[1, 2, 3], [4, 5, 6], [7, 8, 9]], \n",
    "    index=pd.Index(['x6', 'y6', 'z6'], name='i6'), \n",
    "    columns=pd.Index(['a6', 'b6', 'c6'], name='c6'))\n",
    "print(df4)\n",
    "\n",
    "multi_i = pd.MultiIndex.from_arrays([['x7', 'y7', 'z7'], ['x8', 'y8', 'z8']], names=['i7', 'i8']) \n",
    "multi_c = pd.MultiIndex.from_arrays([['a7', 'b7', 'c7'], ['a8', 'b8', 'c8']], names=['c7', 'c8'])\n",
    "df5 = pd.DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]], index=multi_i, columns=multi_c)\n",
    "print(df5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## config"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\"Config keys are frozen: key 'd' not found\"\n",
      "\"Config keys are frozen: key 'd' not found\"\n"
     ]
    }
   ],
   "source": [
    "conf = config.Config({'a': 0, 'b': {'c': 1}}, frozen_keys=False)\n",
    "conf['b']['d'] = 2\n",
    "\n",
    "conf = config.Config({'a': 0, 'b': {'c': 1}}, frozen_keys=True)\n",
    "conf['a'] = 2\n",
    "\n",
    "try:\n",
    "    conf['d'] = 2\n",
    "except Exception as e:\n",
    "    print(e)\n",
    "\n",
    "try:\n",
    "    conf.update(d=2)\n",
    "except Exception as e:\n",
    "    print(e)\n",
    "    \n",
    "conf.update(d=2, force=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Config is read-only\n",
      "Config is read-only\n",
      "Config is read-only\n",
      "Config is read-only\n",
      "Config is read-only\n",
      "Config is read-only\n",
      "{'a': 0, 'b': {'d': 2}}\n"
     ]
    }
   ],
   "source": [
    "conf = config.Config({'a': 0, 'b': {'c': 1}}, readonly=True)\n",
    "\n",
    "try:\n",
    "    conf['a'] = 2\n",
    "except Exception as e:\n",
    "    print(e)\n",
    "    \n",
    "try:\n",
    "    del conf['a']\n",
    "except Exception as e:\n",
    "    print(e)\n",
    "    \n",
    "try:\n",
    "    conf.pop('a')\n",
    "except Exception as e:\n",
    "    print(e)\n",
    "    \n",
    "try:\n",
    "    conf.popitem()\n",
    "except Exception as e:\n",
    "    print(e)\n",
    "    \n",
    "try:\n",
    "    conf.clear()\n",
    "except Exception as e:\n",
    "    print(e)\n",
    "    \n",
    "try:\n",
    "    conf.update(a=2)\n",
    "except Exception as e:\n",
    "    print(e)\n",
    "    \n",
    "print(conf.merge_with(dict(b=dict(d=2))))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'a': 1, 'b': 2}\n",
      "{'a': 2}\n",
      "{'a': {'b': 2, 'c': 3}}\n",
      "{'a': {'b': 3}}\n"
     ]
    }
   ],
   "source": [
    "print(config.merge_dicts({'a': 1}, {'b': 2}))\n",
    "print(config.merge_dicts({'a': 1}, {'a': 2}))\n",
    "print(config.merge_dicts({'a': {'b': 2}}, {'a': {'c': 3}}))\n",
    "print(config.merge_dicts({'a': {'b': 2}}, {'a': {'b': 3}}))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Config({\n",
      "    \"a\": 1,\n",
      "    \"b\": 2\n",
      "})\n",
      "Config({\n",
      "    \"a\": 1,\n",
      "    \"b\": 3\n",
      "})\n",
      "Config({\n",
      "    \"a\": 1,\n",
      "    \"b\": 2,\n",
      "    \"c\": 4\n",
      "})\n"
     ]
    }
   ],
   "source": [
    "class H(config.Configured):\n",
    "    def __init__(self, a, b=2, **kwargs):\n",
    "        super().__init__(a=a, b=b, **kwargs)\n",
    "        \n",
    "print(H(1).config)\n",
    "print(H(1).copy(b=3).config)\n",
    "print(H(1).copy(c=4).config)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## decorators"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "class\n",
      "instance\n"
     ]
    }
   ],
   "source": [
    "class G():\n",
    "    @decorators.class_or_instancemethod\n",
    "    def g(self_or_cls):\n",
    "        if isinstance(self_or_cls, type):\n",
    "            print(\"class\")\n",
    "        else:\n",
    "            print(\"instance\")\n",
    "            \n",
    "G.g()\n",
    "G().g()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'hello': 'world', 'hello2': 'world2'}"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "class G():\n",
    "    @decorators.cached_property(hello=\"world\", hello2=\"world2\")\n",
    "    def cache_me(self): return np.random.uniform(size=(10000, 10000))\n",
    "    \n",
    "G.cache_me.flags"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "g = G()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.19 s, sys: 128 ms, total: 1.32 s\n",
      "Wall time: 1.32 s\n",
      "CPU times: user 24 µs, sys: 0 ns, total: 24 µs\n",
      "Wall time: 26 µs\n"
     ]
    }
   ],
   "source": [
    "%time _ = g.cache_me\n",
    "%time _ = g.cache_me"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['__cached_cache_me',\n",
       " '__class__',\n",
       " '__delattr__',\n",
       " '__dict__',\n",
       " '__dir__',\n",
       " '__doc__',\n",
       " '__eq__',\n",
       " '__format__',\n",
       " '__ge__',\n",
       " '__getattribute__',\n",
       " '__gt__',\n",
       " '__hash__',\n",
       " '__init__',\n",
       " '__init_subclass__',\n",
       " '__le__',\n",
       " '__lt__',\n",
       " '__module__',\n",
       " '__ne__',\n",
       " '__new__',\n",
       " '__reduce__',\n",
       " '__reduce_ex__',\n",
       " '__repr__',\n",
       " '__setattr__',\n",
       " '__sizeof__',\n",
       " '__str__',\n",
       " '__subclasshook__',\n",
       " '__weakref__',\n",
       " 'cache_me']"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dir(g)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.2 s, sys: 165 ms, total: 1.37 s\n",
      "Wall time: 1.44 s\n",
      "CPU times: user 41 µs, sys: 162 µs, total: 203 µs\n",
      "Wall time: 204 µs\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "%time _ = g.cache_me\n",
    "%time _ = g.cache_me"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.19 s, sys: 168 ms, total: 1.36 s\n",
      "Wall time: 1.36 s\n",
      "CPU times: user 1.2 s, sys: 176 ms, total: 1.37 s\n",
      "Wall time: 1.37 s\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['blacklist'].append(vbt.CacheCondition(instance=g, func='cache_me'))\n",
    "%time _ = g.cache_me\n",
    "%time _ = g.cache_me\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.2 s, sys: 180 ms, total: 1.38 s\n",
      "Wall time: 1.38 s\n",
      "CPU times: user 1.19 s, sys: 168 ms, total: 1.36 s\n",
      "Wall time: 1.36 s\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['blacklist'].append(vbt.CacheCondition(func='cache_me'))\n",
    "%time _ = g.cache_me\n",
    "%time _ = g.cache_me\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.18 s, sys: 165 ms, total: 1.35 s\n",
      "Wall time: 1.35 s\n",
      "CPU times: user 1.19 s, sys: 172 ms, total: 1.36 s\n",
      "Wall time: 1.36 s\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['blacklist'].append(vbt.CacheCondition(instance=g))\n",
    "%time _ = g.cache_me\n",
    "%time _ = g.cache_me\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.18 s, sys: 164 ms, total: 1.35 s\n",
      "Wall time: 1.35 s\n",
      "CPU times: user 1.18 s, sys: 161 ms, total: 1.35 s\n",
      "Wall time: 1.34 s\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['blacklist'].append(vbt.CacheCondition(cls=G))\n",
    "%time _ = g.cache_me\n",
    "%time _ = g.cache_me\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.17 s, sys: 162 ms, total: 1.33 s\n",
      "Wall time: 1.33 s\n",
      "CPU times: user 1.17 s, sys: 158 ms, total: 1.33 s\n",
      "Wall time: 1.33 s\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['blacklist'].append(vbt.CacheCondition(cls='G'))\n",
    "%time _ = g.cache_me\n",
    "%time _ = g.cache_me\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.17 s, sys: 161 ms, total: 1.34 s\n",
      "Wall time: 1.33 s\n",
      "CPU times: user 23 µs, sys: 1 µs, total: 24 µs\n",
      "Wall time: 26.2 µs\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['blacklist'].append(vbt.CacheCondition(cls='g'))\n",
    "%time _ = g.cache_me\n",
    "%time _ = g.cache_me\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.18 s, sys: 161 ms, total: 1.34 s\n",
      "Wall time: 1.34 s\n",
      "CPU times: user 1.17 s, sys: 159 ms, total: 1.33 s\n",
      "Wall time: 1.33 s\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['blacklist'].append(vbt.CacheCondition(flags={'hello': 'world'}))\n",
    "%time _ = g.cache_me\n",
    "%time _ = g.cache_me\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.18 s, sys: 161 ms, total: 1.34 s\n",
      "Wall time: 1.34 s\n",
      "CPU times: user 1.18 s, sys: 161 ms, total: 1.34 s\n",
      "Wall time: 1.34 s\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['blacklist'].append(vbt.CacheCondition(flags={'hello': 'world', 'hello2': 'world2'}))\n",
    "%time _ = g.cache_me\n",
    "%time _ = g.cache_me\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.19 s, sys: 163 ms, total: 1.36 s\n",
      "Wall time: 1.36 s\n",
      "CPU times: user 26 µs, sys: 0 ns, total: 26 µs\n",
      "Wall time: 26.9 µs\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['blacklist'].append(vbt.CacheCondition(flags={'hello': 'world', 'hello2': 'world2', 'hello3': 'world3'}))\n",
    "%time _ = g.cache_me\n",
    "%time _ = g.cache_me\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.17 s, sys: 163 ms, total: 1.33 s\n",
      "Wall time: 1.33 s\n",
      "CPU times: user 1.17 s, sys: 169 ms, total: 1.34 s\n",
      "Wall time: 1.34 s\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['enabled'] = False\n",
    "%time _ = g.cache_me\n",
    "%time _ = g.cache_me\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.17 s, sys: 167 ms, total: 1.34 s\n",
      "Wall time: 1.34 s\n",
      "CPU times: user 28 µs, sys: 0 ns, total: 28 µs\n",
      "Wall time: 30 µs\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['enabled'] = False\n",
    "vbt.settings.caching['whitelist'].append(vbt.CacheCondition(instance=g, func='cache_me'))\n",
    "%time _ = g.cache_me\n",
    "%time _ = g.cache_me\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.18 s, sys: 162 ms, total: 1.34 s\n",
      "Wall time: 1.34 s\n",
      "CPU times: user 28 µs, sys: 1 µs, total: 29 µs\n",
      "Wall time: 29.1 µs\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['enabled'] = False\n",
    "vbt.settings.caching['whitelist'].append(vbt.CacheCondition(func='cache_me'))\n",
    "%time _ = g.cache_me\n",
    "%time _ = g.cache_me\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.19 s, sys: 163 ms, total: 1.35 s\n",
      "Wall time: 1.35 s\n",
      "CPU times: user 27 µs, sys: 0 ns, total: 27 µs\n",
      "Wall time: 27.9 µs\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['enabled'] = False\n",
    "vbt.settings.caching['whitelist'].append(vbt.CacheCondition(instance=g))\n",
    "%time _ = g.cache_me\n",
    "%time _ = g.cache_me\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.17 s, sys: 167 ms, total: 1.34 s\n",
      "Wall time: 1.34 s\n",
      "CPU times: user 25 µs, sys: 0 ns, total: 25 µs\n",
      "Wall time: 26 µs\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['enabled'] = False\n",
    "vbt.settings.caching['whitelist'].append(vbt.CacheCondition(cls=G))\n",
    "%time _ = g.cache_me\n",
    "%time _ = g.cache_me\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.19 s, sys: 164 ms, total: 1.35 s\n",
      "Wall time: 1.35 s\n",
      "CPU times: user 28 µs, sys: 0 ns, total: 28 µs\n",
      "Wall time: 29.1 µs\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['enabled'] = False\n",
    "vbt.settings.caching['whitelist'].append(vbt.CacheCondition(cls='G'))\n",
    "%time _ = g.cache_me\n",
    "%time _ = g.cache_me\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.19 s, sys: 166 ms, total: 1.36 s\n",
      "Wall time: 1.36 s\n",
      "CPU times: user 26 µs, sys: 0 ns, total: 26 µs\n",
      "Wall time: 27.9 µs\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['enabled'] = False\n",
    "vbt.settings.caching['whitelist'].append(vbt.CacheCondition(flags={'hello': 'world'}))\n",
    "%time _ = g.cache_me\n",
    "%time _ = g.cache_me\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'hello': 'world', 'hello2': 'world2'}"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "class G():\n",
    "    @decorators.cached_method(hello=\"world\", hello2=\"world2\")\n",
    "    def cache_me(self, a): return np.random.uniform(size=(10000, 10000)) * a\n",
    "\n",
    "G.cache_me.flags"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "g = G()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.21 s, sys: 193 ms, total: 1.4 s\n",
      "Wall time: 1.41 s\n",
      "CPU times: user 31 µs, sys: 1 µs, total: 32 µs\n",
      "Wall time: 31.9 µs\n"
     ]
    }
   ],
   "source": [
    "%time _ = g.cache_me(2)\n",
    "%time _ = g.cache_me(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['__cached_cache_me',\n",
       " '__class__',\n",
       " '__delattr__',\n",
       " '__dict__',\n",
       " '__dir__',\n",
       " '__doc__',\n",
       " '__eq__',\n",
       " '__format__',\n",
       " '__ge__',\n",
       " '__getattribute__',\n",
       " '__gt__',\n",
       " '__hash__',\n",
       " '__init__',\n",
       " '__init_subclass__',\n",
       " '__le__',\n",
       " '__lt__',\n",
       " '__module__',\n",
       " '__ne__',\n",
       " '__new__',\n",
       " '__reduce__',\n",
       " '__reduce_ex__',\n",
       " '__repr__',\n",
       " '__setattr__',\n",
       " '__sizeof__',\n",
       " '__str__',\n",
       " '__subclasshook__',\n",
       " '__weakref__',\n",
       " 'cache_me']"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dir(g)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.23 s, sys: 188 ms, total: 1.41 s\n",
      "Wall time: 1.41 s\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "%time _ = g.cache_me(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.23 s, sys: 189 ms, total: 1.42 s\n",
      "Wall time: 1.41 s\n",
      "CPU times: user 31 µs, sys: 1 µs, total: 32 µs\n",
      "Wall time: 33.1 µs\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "G.cache_me.disabled = True\n",
    "%time _ = g.cache_me(2)\n",
    "%time _ = g.cache_me(2)\n",
    "G.cache_me.disabled = False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.23 s, sys: 183 ms, total: 1.41 s\n",
      "Wall time: 1.41 s\n",
      "CPU times: user 1.23 s, sys: 190 ms, total: 1.42 s\n",
      "Wall time: 1.42 s\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['blacklist'].append(vbt.CacheCondition(func=g.cache_me))\n",
    "%time _ = g.cache_me(2)\n",
    "%time _ = g.cache_me(2)\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.21 s, sys: 187 ms, total: 1.4 s\n",
      "Wall time: 1.4 s\n",
      "CPU times: user 1.22 s, sys: 184 ms, total: 1.41 s\n",
      "Wall time: 1.41 s\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['blacklist'].append(vbt.CacheCondition(instance=g, func='cache_me'))\n",
    "%time _ = g.cache_me(2)\n",
    "%time _ = g.cache_me(2)\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.23 s, sys: 185 ms, total: 1.41 s\n",
      "Wall time: 1.41 s\n",
      "CPU times: user 1.23 s, sys: 181 ms, total: 1.41 s\n",
      "Wall time: 1.41 s\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['blacklist'].append(vbt.CacheCondition(func='cache_me'))\n",
    "%time _ = g.cache_me(2)\n",
    "%time _ = g.cache_me(2)\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.23 s, sys: 186 ms, total: 1.41 s\n",
      "Wall time: 1.41 s\n",
      "CPU times: user 1.23 s, sys: 189 ms, total: 1.41 s\n",
      "Wall time: 1.41 s\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['blacklist'].append(vbt.CacheCondition(instance=g))\n",
    "%time _ = g.cache_me(2)\n",
    "%time _ = g.cache_me(2)\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.23 s, sys: 188 ms, total: 1.41 s\n",
      "Wall time: 1.41 s\n",
      "CPU times: user 1.23 s, sys: 183 ms, total: 1.41 s\n",
      "Wall time: 1.41 s\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['blacklist'].append(vbt.CacheCondition(cls=G))\n",
    "%time _ = g.cache_me(2)\n",
    "%time _ = g.cache_me(2)\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.23 s, sys: 191 ms, total: 1.42 s\n",
      "Wall time: 1.42 s\n",
      "CPU times: user 1.23 s, sys: 182 ms, total: 1.41 s\n",
      "Wall time: 1.41 s\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['blacklist'].append(vbt.CacheCondition(cls='G'))\n",
    "%time _ = g.cache_me(2)\n",
    "%time _ = g.cache_me(2)\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.22 s, sys: 185 ms, total: 1.41 s\n",
      "Wall time: 1.41 s\n",
      "CPU times: user 34 µs, sys: 1e+03 ns, total: 35 µs\n",
      "Wall time: 35 µs\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['blacklist'].append(vbt.CacheCondition(cls='g'))\n",
    "%time _ = g.cache_me(2)\n",
    "%time _ = g.cache_me(2)\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.21 s, sys: 186 ms, total: 1.4 s\n",
      "Wall time: 1.4 s\n",
      "CPU times: user 1.24 s, sys: 221 ms, total: 1.46 s\n",
      "Wall time: 1.46 s\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['blacklist'].append(vbt.CacheCondition(flags={'hello': 'world'}))\n",
    "%time _ = g.cache_me(2)\n",
    "%time _ = g.cache_me(2)\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.21 s, sys: 191 ms, total: 1.4 s\n",
      "Wall time: 1.4 s\n",
      "CPU times: user 1.23 s, sys: 203 ms, total: 1.43 s\n",
      "Wall time: 1.43 s\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['blacklist'].append(vbt.CacheCondition(flags={'hello': 'world', 'hello2': 'world2'}))\n",
    "%time _ = g.cache_me(2)\n",
    "%time _ = g.cache_me(2)\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.23 s, sys: 200 ms, total: 1.43 s\n",
      "Wall time: 1.43 s\n",
      "CPU times: user 34 µs, sys: 0 ns, total: 34 µs\n",
      "Wall time: 35 µs\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['blacklist'].append(vbt.CacheCondition(flags={'hello': 'world', 'hello2': 'world2', 'hello3': 'world3'}))\n",
    "%time _ = g.cache_me(2)\n",
    "%time _ = g.cache_me(2)\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.23 s, sys: 196 ms, total: 1.42 s\n",
      "Wall time: 1.42 s\n",
      "CPU times: user 1.23 s, sys: 185 ms, total: 1.41 s\n",
      "Wall time: 1.41 s\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['enabled'] = False\n",
    "%time _ = g.cache_me(2)\n",
    "%time _ = g.cache_me(2)\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.23 s, sys: 201 ms, total: 1.43 s\n",
      "Wall time: 1.43 s\n",
      "CPU times: user 37 µs, sys: 1e+03 ns, total: 38 µs\n",
      "Wall time: 37.9 µs\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['enabled'] = False\n",
    "vbt.settings.caching['whitelist'].append(vbt.CacheCondition(instance=g, func='cache_me'))\n",
    "%time _ = g.cache_me(2)\n",
    "%time _ = g.cache_me(2)\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.24 s, sys: 248 ms, total: 1.49 s\n",
      "Wall time: 1.49 s\n",
      "CPU times: user 37 µs, sys: 1 µs, total: 38 µs\n",
      "Wall time: 38.9 µs\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['enabled'] = False\n",
    "vbt.settings.caching['whitelist'].append(vbt.CacheCondition(func='cache_me'))\n",
    "%time _ = g.cache_me(2)\n",
    "%time _ = g.cache_me(2)\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.26 s, sys: 276 ms, total: 1.53 s\n",
      "Wall time: 1.53 s\n",
      "CPU times: user 36 µs, sys: 1 µs, total: 37 µs\n",
      "Wall time: 37.2 µs\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['enabled'] = False\n",
    "vbt.settings.caching['whitelist'].append(vbt.CacheCondition(instance=g))\n",
    "%time _ = g.cache_me(2)\n",
    "%time _ = g.cache_me(2)\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.23 s, sys: 211 ms, total: 1.44 s\n",
      "Wall time: 1.44 s\n",
      "CPU times: user 36 µs, sys: 0 ns, total: 36 µs\n",
      "Wall time: 37.7 µs\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['enabled'] = False\n",
    "vbt.settings.caching['whitelist'].append(vbt.CacheCondition(cls=G))\n",
    "%time _ = g.cache_me(2)\n",
    "%time _ = g.cache_me(2)\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.23 s, sys: 206 ms, total: 1.44 s\n",
      "Wall time: 1.43 s\n",
      "CPU times: user 36 µs, sys: 0 ns, total: 36 µs\n",
      "Wall time: 37.9 µs\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['enabled'] = False\n",
    "vbt.settings.caching['whitelist'].append(vbt.CacheCondition(cls='G'))\n",
    "%time _ = g.cache_me(2)\n",
    "%time _ = g.cache_me(2)\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.23 s, sys: 193 ms, total: 1.42 s\n",
      "Wall time: 1.42 s\n",
      "CPU times: user 34 µs, sys: 1e+03 ns, total: 35 µs\n",
      "Wall time: 34.8 µs\n"
     ]
    }
   ],
   "source": [
    "G.cache_me.clear_cache(g)\n",
    "vbt.settings.caching['enabled'] = False\n",
    "vbt.settings.caching['whitelist'].append(vbt.CacheCondition(flags={'hello': 'world'}))\n",
    "%time _ = g.cache_me(2)\n",
    "%time _ = g.cache_me(2)\n",
    "vbt.settings.caching.reset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.23 s, sys: 161 ms, total: 1.39 s\n",
      "Wall time: 1.39 s\n",
      "CPU times: user 1.27 s, sys: 302 ms, total: 1.57 s\n",
      "Wall time: 1.78 s\n"
     ]
    }
   ],
   "source": [
    "# Non-hashable arguments won't cache\n",
    "%time _ = g.cache_me(np.asarray(2))\n",
    "%time _ = g.cache_me(np.asarray(2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## checks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False\n",
      "False\n",
      "True\n",
      "False\n"
     ]
    }
   ],
   "source": [
    "print(checks.is_series(v1))\n",
    "print(checks.is_series(a1))\n",
    "print(checks.is_series(sr1))\n",
    "print(checks.is_series(df1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False\n",
      "False\n",
      "False\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "print(checks.is_frame(v1))\n",
    "print(checks.is_frame(a1))\n",
    "print(checks.is_frame(sr1))\n",
    "print(checks.is_frame(df1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False\n",
      "False\n",
      "True\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "print(checks.is_pandas(v1))\n",
    "print(checks.is_pandas(a1))\n",
    "print(checks.is_pandas(sr1))\n",
    "print(checks.is_pandas(df1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False\n",
      "True\n",
      "True\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "print(checks.is_any_array(v1))\n",
    "print(checks.is_any_array(a1))\n",
    "print(checks.is_any_array(sr1))\n",
    "print(checks.is_any_array(df1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "print(checks.is_numba_func(lambda x: x))\n",
    "print(checks.is_numba_func(njit(lambda x: x)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "False\n"
     ]
    }
   ],
   "source": [
    "print(checks.is_hashable(2))\n",
    "print(checks.is_hashable(np.asarray(2)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [],
   "source": [
    "checks.assert_in(0, (0, 1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [],
   "source": [
    "checks.assert_numba_func(njit(lambda x: x))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [],
   "source": [
    "checks.assert_not_none(v1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [],
   "source": [
    "checks.assert_type(v1, int)\n",
    "checks.assert_type(a1, np.ndarray)\n",
    "checks.assert_type(sr1, (np.ndarray, pd.Series))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {},
   "outputs": [],
   "source": [
    "checks.assert_type_equal(v1, v1)\n",
    "checks.assert_type_equal(a1, a2)\n",
    "checks.assert_type_equal(sr1, sr1)\n",
    "checks.assert_type_equal(df1, df2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [],
   "source": [
    "checks.assert_dtype(a1, np.integer)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [],
   "source": [
    "checks.assert_dtype_equal(v1, a1)\n",
    "checks.assert_dtype_equal(a1, df1)\n",
    "checks.assert_dtype_equal(df1, df2)\n",
    "checks.assert_dtype_equal(df2, df3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [],
   "source": [
    "checks.assert_ndim(v1, 0)\n",
    "checks.assert_ndim(a1, 1)\n",
    "checks.assert_ndim(df1, 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [],
   "source": [
    "checks.assert_len_equal([[1]], [[2]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [],
   "source": [
    "checks.assert_shape_equal(a1, sr1)\n",
    "checks.assert_shape_equal(df2, df4, axis=0)\n",
    "checks.assert_shape_equal(df3, df4, axis=1)\n",
    "checks.assert_shape_equal(df2, df3, axis=(0, 1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {},
   "outputs": [],
   "source": [
    "checks.assert_index_equal(df3.index, df3.index)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [],
   "source": [
    "checks.assert_meta_equal(df3, df3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [],
   "source": [
    "checks.assert_array_equal(df3, df3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [],
   "source": [
    "checks.assert_level_not_exists(df3.columns, 'a')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "state": {},
    "version_major": 2,
    "version_minor": 0
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
